//===--- PIL.cpp - Implements random PIL functionality --------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/lang/FormalLinkage.h"
#include "polarphp/pil/lang/PILModule.h"
#include "polarphp/pil/lang/PILBuilder.h"
#include "polarphp/pil/lang/PILDeclRef.h"
#include "polarphp/pil/lang/PILType.h"
#include "polarphp/pil/lang/PILUndef.h"
#include "polarphp/ast/AstContext.h"
#include "polarphp/ast/AnyFunctionRef.h"
#include "polarphp/ast/Decl.h"
#include "polarphp/ast/GenericEnvironment.h"
#include "polarphp/ast/Pattern.h"
#include "polarphp/ast/ParameterList.h"
#include "polarphp/ast/InterfaceConformance.h"
#include "polarphp/clangimporter/ClangModule.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"

using namespace polar;

FormalLinkage polar::getDeclLinkage(const ValueDecl *D) {
   const DeclContext *fileContext = D->getDeclContext()->getModuleScopeContext();

   // Clang declarations are public and can't be assured of having a
   // unique defining location.
   if (isa<ClangModuleUnit>(fileContext))
      return FormalLinkage::PublicNonUnique;

   switch (D->getEffectiveAccess()) {
      case AccessLevel::Public:
      case AccessLevel::Open:
         return FormalLinkage::PublicUnique;
      case AccessLevel::Interface:
         return FormalLinkage::HiddenUnique;
      case AccessLevel::FilePrivate:
      case AccessLevel::Private:
         return FormalLinkage::Private;
   }

   llvm_unreachable("Unhandled access level in switch.");
}

PILLinkage polar::getPILLinkage(FormalLinkage linkage,
                                ForDefinition_t forDefinition) {
   switch (linkage) {
      case FormalLinkage::PublicUnique:
         return (forDefinition ? PILLinkage::Public : PILLinkage::PublicExternal);

      case FormalLinkage::PublicNonUnique:
         // FIXME: any place we have to do this that actually requires
         // uniqueness is buggy.
         return (forDefinition ? PILLinkage::Shared : PILLinkage::PublicExternal);

      case FormalLinkage::HiddenUnique:
         return (forDefinition ? PILLinkage::Hidden : PILLinkage::HiddenExternal);

      case FormalLinkage::Private:
         return PILLinkage::Private;
   }
   llvm_unreachable("bad formal linkage");
}

PILLinkage
polar::getLinkageForInterfaceConformance(const RootInterfaceConformance *C,
                                         ForDefinition_t definition) {
   // If the conformance was synthesized by the ClangImporter, give it
   // shared linkage.
   if (isa<ClangModuleUnit>(C->getDeclContext()->getModuleScopeContext()))
      return PILLinkage::Shared;

   auto typeDecl = C->getType()->getNominalOrBoundGenericNominal();
   AccessLevel access = std::min(C->getInterface()->getEffectiveAccess(),
                                 typeDecl->getEffectiveAccess());
   switch (access) {
      case AccessLevel::Private:
      case AccessLevel::FilePrivate:
         return (definition ? PILLinkage::Private : PILLinkage::PrivateExternal);

      case AccessLevel::Interface:
         return (definition ? PILLinkage::Hidden : PILLinkage::HiddenExternal);

      default:
         return (definition ? PILLinkage::Public : PILLinkage::PublicExternal);
   }
}

bool PILModule::isTypeMetadataAccessible(CanType type) {
   // PILModules built for the debugger have special powers to access metadata
   // for types in other files/modules.
   if (getAstContext().LangOpts.DebuggerSupport)
      return true;

   assert(type->isLegalFormalType());

   return !type.findIf([&](CanType type) {
      // Note that this function returns true if the type is *illegal* to use.

      // Ignore non-nominal types.
      auto decl = type.getNominalOrBoundGenericNominal();
      if (!decl)
         return false;

      // Check whether the declaration is inaccessible from the current context.
      switch (getDeclLinkage(decl)) {

         // Public declarations are accessible from everywhere.
         case FormalLinkage::PublicUnique:
         case FormalLinkage::PublicNonUnique:
            return false;

            // Hidden declarations are inaccessible from different modules.
         case FormalLinkage::HiddenUnique:
            return (decl->getModuleContext() != getPolarphpModule());

            // Private declarations are inaccessible from different files unless
            // this is WMO and we're in the same module.
         case FormalLinkage::Private: {
            // The only time we don't have an associated DC is in the
            // integrated REPL, where we also don't have a concept of other
            // source files within the current module.
            if (!AssociatedDeclContext)
               return (decl->getModuleContext() != getPolarphpModule());

            // The associated DC should be either a SourceFile or, in WMO mode,
            // a ModuleDecl.  In the WMO modes, IRGen will ensure that private
            // declarations are usable throughout the module.  Therefore, in
            // either case we just need to make sure that the declaration comes
            // from within the associated DC.
            auto declDC = decl->getDeclContext();
            return !(declDC == AssociatedDeclContext ||
                     declDC->isChildContextOf(AssociatedDeclContext));
         }
      }
      llvm_unreachable("bad linkage");
   });
}

/// Answer whether IRGen's emitTypeMetadataForLayout can fetch metadata for
/// a type, which is the necessary condition for being able to do value
/// operations on the type using dynamic metadata.
static bool isTypeMetadataForLayoutAccessible(PILModule &M, PILType type) {
   // Look through types that aren't necessarily legal formal types:

   //   - tuples
   if (auto tupleType = type.getAs<TupleType>()) {
      for (auto index : indices(tupleType.getElementTypes())) {
         if (!isTypeMetadataForLayoutAccessible(M, type.getTupleElementType(index)))
            return false;
      }
      return true;
   }

   //   - optionals
   if (auto objType = type.getOptionalObjectType()) {
      return isTypeMetadataForLayoutAccessible(M, objType);
   }

   //   - function types
   if (type.is<PILFunctionType>())
      return true;

   //   - metatypes
   if (type.is<AnyMetatypeType>())
      return true;

   // Otherwise, check that we can fetch the type metadata.
   return M.isTypeMetadataAccessible(type.getAstType());

}

/// Can we perform value operations on the given type?  We have no way
/// of doing value operations on resilient-layout types from other modules
/// that are ABI-private to their defining module.  But if the type is not
/// ABI-private, we can always at least fetch its metadata and use the
/// value witness table stored there.
bool PILModule::isTypeABIAccessible(PILType type,
                                    TypeExpansionContext forExpansion) {
   // Fixed-ABI types can have value operations done without metadata.
   if (Types.getTypeLowering(type, forExpansion).isFixedABI())
      return true;

   assert(!type.is<ReferenceStorageType>() &&
          !type.is<PILFunctionType>() &&
          !type.is<AnyMetatypeType>() &&
          "unexpected PIL lowered-only type with non-fixed layout");

   // Otherwise, we need to be able to fetch layout-metadata for the type.
   return isTypeMetadataForLayoutAccessible(*this, type);
}

bool AbstractStorageDecl::exportsPropertyDescriptor() const {
   // The storage needs a descriptor if it sits at a module's ABI boundary,
   // meaning it has public linkage.

   // TODO: Global and static properties ought to eventually be referenceable
   // as key paths from () or T.Type too.
   if (!getDeclContext()->isTypeContext() || isStatic())
      return false;

   // Interface requirements do not need property descriptors.
   if (isa<InterfaceDecl>(getDeclContext()))
      return false;

   // FIXME: We should support properties and subscripts with '_read' accessors;
   // 'get' is not part of the opaque accessor set there.
   auto *getter = getOpaqueAccessor(AccessorKind::Get);
   if (!getter)
      return false;

   // If the getter is mutating, we cannot form a keypath to it at all.
   if (isGetterMutating())
      return false;

   // If the storage is an ABI-compatible override of another declaration, we're
   // not going to be emitting a property descriptor either.
   if (!isValidKeyPathComponent())
      return false;

   // TODO: If previous versions of an ABI-stable binary needed the descriptor,
   // then we still do.

   // Check the linkage of the declaration.
   auto getterLinkage = PILDeclRef(getter).getLinkage(ForDefinition);

   switch (getterLinkage) {
      case PILLinkage::Public:
      case PILLinkage::PublicNonABI:
         // We may need a descriptor.
         break;

      case PILLinkage::Shared:
      case PILLinkage::Private:
      case PILLinkage::Hidden:
         // Don't need a public descriptor.
         return false;

      case PILLinkage::HiddenExternal:
      case PILLinkage::PrivateExternal:
      case PILLinkage::PublicExternal:
      case PILLinkage::SharedExternal:
         llvm_unreachable("should be definition linkage?");
   }

   // Subscripts with inout arguments (FIXME)and reabstracted arguments(/FIXME)
   // don't have descriptors either.
   if (auto sub = dyn_cast<SubscriptDecl>(this)) {
      for (auto *index : *sub->getIndices()) {
         // Keypaths can't capture inout indices.
         if (index->isInOut())
            return false;

         auto indexTy = index->getInterfaceType()
            ->getCanonicalType(sub->getGenericSignatureOfContext());

         // TODO: Handle reabstraction and tuple explosion in thunk generation.
         // This wasn't previously a concern because anything that was Hashable
         // had only one abstraction level and no explosion.

         if (isa<TupleType>(indexTy))
            return false;

         auto indexObjTy = indexTy;
         if (auto objTy = indexObjTy.getOptionalObjectType())
            indexObjTy = objTy;

         if (isa<AnyFunctionType>(indexObjTy)
             || isa<AnyMetatypeType>(indexObjTy))
            return false;
      }
   }
   return true;
}
